#include "TrLogger.h"
#include <iostream>
#include <set>
#include "TrUtils.h"
#include <filesystem>

namespace fs = std::filesystem;

static SpdLoggerPtr					gs_rootLogger = nullptr;
static std::set<std::string>		gs_setLoggers;
static thread_local char			gs_buffer[2048];

SpdLoggerPtr TrLogger::create(const char* logger, spdlog::level::level_enum level)
{
	std::string _path = TrUtils::format("{}/logs", fs::current_path().string());
	fs::create_directories(_path);
	auto it = gs_setLoggers.find(logger);

	if (it != gs_setLoggers.end()) 
	{
		return TrLogger::get(logger);
	}
	else 
	{
		try
		{
			auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
			console_sink->set_level(level);
			console_sink->set_pattern(DEFAULT_LOG_PATTERN);

			auto daily_sink = std::make_shared<spdlog::sinks::daily_file_sink_mt>(TrUtils::format("{}/{}.log", _path, logger), 00, 00);
			daily_sink->set_level(level);
			daily_sink->set_pattern(DEFAULT_LOG_PATTERN);

			std::vector<spdlog::sink_ptr> sinks{ console_sink, daily_sink };

			auto _logger = std::make_shared<spdlog::logger>(logger, sinks.begin(), sinks.end());
			_logger->set_level(level);
			_logger->set_pattern(DEFAULT_LOG_PATTERN);
			spdlog::register_logger(_logger);
			spdlog::flush_every(std::chrono::seconds(3));

			gs_setLoggers.insert(logger);

			return _logger;
		}
		catch (const spdlog::spdlog_ex& ex)
		{
			std::cerr << "Logger init failed: " << ex.what() << std::endl;
		}
	}

	return SpdLoggerPtr();
}

SpdLoggerPtr TrLogger::get(const char * logger)
{
	return spdlog::get(logger);
}

void TrLogger::trace(const char* msg)
{
	TrLogger::getInstance();
	gs_rootLogger->trace(msg);
}

void TrLogger::debug(const char* msg)
{
	TrLogger::getInstance();
	gs_rootLogger->debug(msg);
}

void TrLogger::info(const char* msg)
{
	TrLogger::getInstance();
	gs_rootLogger->info(msg);
}

void TrLogger::warn(const char* msg)
{
	TrLogger::getInstance();
	gs_rootLogger->warn(msg);
}

void TrLogger::error(const char* msg)
{
	TrLogger::getInstance();
	gs_rootLogger->error(msg);
}

void TrLogger::fatal(const char* msg)
{
	TrLogger::getInstance();
	gs_rootLogger->critical(msg);
	exit(0);
}

TrLogger::TrLogger()
{
	gs_rootLogger = TrLogger::create("root", spdlog::level::debug);
	spdlog::set_default_logger(gs_rootLogger);
}

template<typename ...Args>
inline void TrLogger::trace(const char* format, const Args & ...args)
{
	TrLogger::getInstance();
	TrUtils::format(gs_buffer, format, args);
	gs_rootLogger->trace(gs_buffer);
}

template<typename ...Args>
inline void TrLogger::debug(const char* format, const Args & ...args)
{
	TrLogger::getInstance();
	TrUtils::format(gs_buffer, format, args);
	gs_rootLogger->debug(gs_buffer);
}

template<typename ...Args>
inline void TrLogger::info(const char* format, const Args & ...args)
{
	TrLogger::getInstance();
	TrUtils::format(gs_buffer, format, args);
	gs_rootLogger->info(gs_buffer);
}

template<typename ...Args>
inline void TrLogger::warn(const char* format, const Args & ...args)
{
	TrLogger::getInstance();
	TrUtils::format(gs_buffer, format, args);
	gs_rootLogger->warn(gs_buffer);
}

template<typename ...Args>
inline void TrLogger::error(const char* format, const Args & ...args)
{
	TrLogger::getInstance();
	TrUtils::format(gs_buffer, format, args);
	gs_rootLogger->error(gs_buffer);
}

template<typename ...Args>
inline void TrLogger::fatal(const char* format, const Args & ...args)
{
	TrLogger::getInstance();
	TrUtils::format(gs_buffer, format, args);
	gs_rootLogger->fatal(gs_buffer);
}
